Kotlin 在物件導向這塊與其他程式語言類似,類別上也包含建構式、函式、屬性、物件宣告等,而所謂類別就像一張藍圖,以蓋房子為例,它只是給予我們如何蓋出房子的細節,並非是一棟蓋好的房子。
在 Kotlin 使用關鍵字 class
宣告類別,主要包含兩類內容:行為
和 資料
,以 類別函數
定義類別的行為
,以 類別屬性
增加類別的 資料
,操作如下範例所示:
我們可以透過下面範例觀察出三件事
class
關鍵字進行定義new
關鍵字進行實現(Instance),而是直接使用類別名稱再加上括號 ( ) 即可取值 get
或 賦值 set
需求時,不需要像 Java 必須在類別裡面建立 getter
與 setter
,這些屬性的 getter
與 setter
是 Kotlin 編譯器為我們自動產生的,讓我們在程式碼中保持fun main() {
// 呼叫類別與呼叫 printName 函數
Person().printName()
}
// 建立 Person 類別(Class)
class Person {
// 建立 userName 屬性(Property)
var userName: String = "user"
// 建立 printName 函數(Function)
fun printName() {
println("Name is $userName")
}
}
在 Kotlin 中,如果一個類別是空的,沒有內容,括號是可以直接省略的
class Person
在 Java 的建構函數(Constructor)可以讓我們建構出多個不同參數的建構函數,但是 Java 每個建構函數都是同級別的,而在 Kotlin 中卻是分成兩級運算子(主建構函數與次建構函數),主建構函數是直接包含在類別名稱之後,次建構函數則是在類別裡面進行實現,我們利用下面範例進行觀察:
下面範例是當我們加入主建構函數時的程式操作狀況
fun main() {
// 在呼叫類別時,傳進去的參數是由主建構函式進行定義
val person = Person("devin", "abc@gmail.com")
print(person.name) // 印出 Devin
}
// 主建構函式定義臨時變數 _name 與 _email
class Person(_name: String, _email: String) {
var name = _name
// 呼叫 capitalize 方法會為數值設定為首字大寫
get() = field.capitalize()
set(value) {
// 呼叫 trim 方法會為傳進來的數值去除空白再儲存
field = value.trim()
}
var email = _email
}
下面範例是加入次建構函數時,增加初始值設定與初始邏輯判斷
fun main() {
val person = Person()
print(person.name)
// 印出下列結果
// 使用者未輸入參數
// 路人甲
}
class Person(_name: String, _email: String) {
var name = _name
// 呼叫 capitalize 方法會為數值設定為首字大寫
get() = field.capitalize()
set(value) {
// 呼叫 trim 方法會為傳進來的數值去除空白再儲存
field = value.trim()
}
var email = _email
// 若主建構函數沒有帶入參數,則自動帶入預設值
constructor() : this(_name = "路人甲", _email = "") {
// 帶入初始邏輯條件
if (name == "路人甲") {
println("使用者未輸入參數")
}
}
}
在上面範例中,我們在次建構函數使用了預設參數方法,但實際上此方法在主建構函數與次建構函數都可以使用,我們也可以將上面範例的預設值改為在主建構函數使用:
fun main() {
val person = Person()
print(person.name)
// 印出下列結果
// 使用者未輸入參數
// 路人甲
}
// 修改主建構函數預設值
class Person(_name: String = "路人甲", _email: String) {
var name = _name
// 呼叫 capitalize 方法會為數值設定為首字大寫
get() = field.capitalize()
set(value) {
// 呼叫 trim 方法會為傳進來的數值去除空白再儲存
field = value.trim()
}
var email = _email
constructor() : this(_email = "") {
// 帶入初始邏輯條件
if (name == "路人甲") {
println("使用者未輸入參數")
}
}
}
除了主次建構函數可設置初始值以外,我們也可以另外為函數定義一個初始化區塊 init,此區塊除了設定初始值以外,也可以進行數值的有效性檢查,可觀察以下範例:
fun main() {
val person = Person("","")
print(person.name)
// 印出下列結果
// 使用者未輸入參數
// 路人甲
}
class Person(_name: String, _email: String) {
var name = _name
// 呼叫 capitalize 方法會為數值設定為首字大寫
get() = field.capitalize()
set(value) {
// 呼叫 trim 方法會為傳進來的數值去除空白再儲存
field = value.trim()
}
var email = _email
// 利用初始化區塊(init)進行初始值設定與有效值檢查
init {
name = "路人甲"
// 帶入參數檢查判斷,若檢查不通過,則拋出 IllegalArgumentException 異常
// 異常結果可看下面圖片顯示
require(name.isNotBlank()) { "使用者未輸入姓名參數" }
}
}
當我們在呼叫類別時,可加入有效值判斷,當檢查不通過時,會如下面圖片呈現出 IllegalArgumentException 的錯誤訊息
前面我們介紹類別(Class)的介紹,我們會發現,假設我們有很多個類別需求,只需多次呼叫類別即可,但產生多個類別的時候,我們可能會遇到一個問題-如何進行類別之間的資料溝通,此時我們就必須要為這樣的需求進行處理。
而假設我們需求只想要使用一個實例(Instance)來管理整個程式的狀態,我們就可以定義一個單例(Singleton)即可,而在 Kotlin 程式語言,根據上述需求,我們可以使用 object 關鍵字進行定義出一個在應用程式中只有它存在的實例
故我們可以歸納出物件(Object)有幾個特性:
物件宣告主要會利用 object
關鍵字進行定義物件(Object),在定義上類似於類別,也有初始區塊、屬性資料等操作方法,相對於類別,物件可自動實例化(Instance),但在 object
無法使用建構函數 Constructor,即無法在初始化時從外部傳遞參數進行實現,但我們利用一個範例進行觀察:
fun main() {
// 呼叫Family object
Family
}
// 建立物件
object Family {
// 建立 object 資料
private val person = Person(_name = "devin")
// 初始區塊
init {
println("歡迎來到 Family 家族")
printFamilyMember()
}
// 建立 object function
private fun printFamilyMember() {
print("目前家族成員有:")
println(person.name)
}
}
原本使用類別進行處理很重要,能夠幫助我們減少重複邏輯一再出現,而將邏輯抽象為一個新事物概念,但往往有時候需求上不見得都會有重複使用的狀況發生,有時候只會有一次性使用,這時候object 就可以幫助我們進行處理這樣的情境,將 object 作為匿名類別
來使用,可參考下面範例:
fun main() {
// 建立 object 匿名類別
val person = object {
var userName: String = "Devin"
var email: String = "abc@gmail.com"
}
println(person.userName) // 輸出「Devin」
}
如果我們在開發上想要把類別實現
與物件初始化
綁在一起,此時就可以考慮使用伴生物件,使用 companion
關鍵字,我們直接利用下面範例進行觀察:
fun main() {
// 呼叫 Person 類別的伴生物件,再呼叫類別方法
Person.data.printUserName()
}
class Person(_name: String = "", _email: String = "") {
val name: String = _name
val email: String = _email
// 建立伴生物件
companion object {
val data = Person("Devin", "abc@gmail.com")
}
fun printUserName(){
println(name)
}
}
// 範例輸出結果為 Devin
在物件導向程式設計中,我們經常會建立專門儲存資料的類別,再將此類別進行實例化物件進行資料溝通,此物件我們會稱為資料傳輸物件
(Data Transfer Object, DTO),在 Kotlin 中特別針對此物件設計一個「資料類別(Data Class)」,我們直接用一個範例來示範:
fun main() {
// 實例化 person 物件
val person1 = Person("devin", "abc@gmail.com")
// 輸出
println("person1 姓名:${person1.name}")
// 資料拷貝
val person2 = person1.copy()
// 輸出
println("person2 姓名:${person2.name}")
// 解構數值
val (name, email) = person2
// 輸出
println("解構輸出姓名:${name}")
// 最後輸出結果
// person1 姓名:devin
// person2 姓名:devin
// 解構輸出姓名:devin
}
// 建立 Person 資料類別
data class Person(
val name: String,
val email: String
)